package com.paradogs.gs.misc;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Function;
import com.paradogs.common.utils.ObjectUtils;
import com.paradogs.common.utils.RandomUtils;
import com.paradogs.common.utils.Wrap;
import com.paradogs.framework.database.ParadogsDBAsyncThread;
import com.paradogs.gs.misc.annotations.GameManager;
import com.paradogs.gs.misc.annotations.NetSyncOwner;
import com.paradogs.gs.modules.OwnerSyncData;
import com.paradogs.gs.modules.Player;
import com.paradogs.gs.modules.GameObject;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * @author: yumi
 * @date: 2024/10/15  18:42
 * @Description: TODO
 */
@Slf4j
public abstract class DBMultiRowManager<D extends ServiceImpl<? extends BaseMapper<E>, E>, E extends BaseMgrMultiEntity> implements IDataManager<Map<Long, E>, E> {

    private GameObject gameObject;

    @Autowired
    private D dao;

    private Map<Long, E> data;

    private Class<E> dataClass;

    private Field[] dataFieldsCache;

    private GameManager gameManager;

    @Setter
    private Map<Long, Boolean> dirtyMap = new HashMap<>();

    public DBMultiRowManager() {
        // 获得泛型的 dao 类型, By Mybatis-Plus-Tools
        Class<?>[] classes = GenericTypeUtils.resolveTypeArguments(getClass(), DBMultiRowManager.class);
        dataClass = (Class<E>) classes[1];
        // 缓存数据反射 待优化缓存
        dataFieldsCache = dataClass.getDeclaredFields();
        for (Field field : dataFieldsCache) { field.setAccessible(true); }
        // 加载注解
        gameManager = AnnotationUtils.findAnnotation(this.getClass(), GameManager.class);
    }

    public Player owner() {
        return (Player) gameObject;
    }

    public void setDirty(Long id, boolean dirty) {
        dirtyMap.put(id, dirty);
    }

    @Override
    public void loadData(GameObject gameObject) {
        try {
            this.gameObject = gameObject;
            this.data = dao.list(Wrap.<E>lbq().eq(E::getUid, gameObject.getId()))
                    .stream().collect(Collectors.toMap(E::getId, e -> e, (t, t2) -> t, LinkedHashMap::new));
            if (this.data.size() == 0) {
                onInitialization(this.data);
                this.data.keySet().forEach(key -> setDirty(key, true));
            }
            this.data.values().forEach(v -> v.setUid(gameObject.getId()));
        } catch (Exception e) {
            Bow.err(String.format("Player loadFromDB err: %s, mgr: %s", e.getMessage(), this.getClass().getSimpleName()));
        }
    }

    @Override
    public void save2DB() {
        if (dirtyMap.isEmpty()) return;
        // clone
        // 将操作加入 db 队列
        for (Map.Entry<Long, Boolean> entry : dirtyMap.entrySet()) {
            Long aid = entry.getKey();
            Boolean dirty = entry.getValue();
            if (!dirty) continue;
            if (aid <= 0 && !this.data.containsKey(-aid)) {
                // 删除
                ParadogsDBAsyncThread.join(owner().getId(), () -> {
                    this.dao.remove(Wrap.<E>lbq()
                            .eq(E::getUid, owner()
                                    .getId()).eq(E::getId, -aid));
                });

            } else if (this.data.containsKey(aid)) {
                // 更新 & 新增
                E data = this.data.get(aid);
                ParadogsDBAsyncThread.join(owner().getId(), () -> {
                    LambdaQueryWrapper<E> wrap = Wrap.<E>lbq()
                            .eq(E::getUid, owner().getId())
                            .eq(E::getId, data.getId());
                    if (!this.dao.update(data, wrap)) { this.dao.save(data); }
                });
            }
        }
        this.dirtyMap = new HashMap<>();
    }

    @Override
    public Map<Long, E> __get__() {
        HashMap<Long, E> newMap = new HashMap<>();
        this.data.forEach((k, v) -> newMap.put(k, ObjectUtils.clone(v)));
        return newMap;
    }

    public E get(Long aid) {
        return ObjectUtils.clone(this.data.get(aid));
    }

    public E get(Integer aid) {
        return ObjectUtils.clone(this.data.get(aid.longValue()));
    }

    public boolean has(Long aid) {
        return get(aid) != null;
    }

    @Override
    public Map<Long, E> __raw__() {
        return this.data;
    }

    @Override
    public void __update__(E data) {
        // 自动状态同步
        try {
            data.setUid(owner().getId());
            this.data.put(data.getId(), data);
            SyncDataUpdater.commit(owner().getId(), data); // 提交到状态同步管理
            setDirty(data.getId(), true);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private final Function<OwnerSyncData, Set<E>> ownerSyncDataGetter = ownerSyncData -> {
        try {
            Method getter = ownerSyncData.getClass().getMethod("get" + dataClass.getSimpleName() + "s");
            return (Set<E>) getter.invoke(ownerSyncData);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
    private final BiConsumer<OwnerSyncData, Set<E>> ownerSyncDataSetter = (ownerSyncData, es) -> {
        try {
            Method setter = ownerSyncData.getClass().getMethod("set" + dataClass.getSimpleName() + "s", Set.class);
            setter.invoke(ownerSyncData, es);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };

    public <T> void update(Long aid, BiConsumer<E, T> consumer, T val) {
        try {
            // 自动状态同步
            setDirty(aid, true);
            consumer.accept(this.data.get(aid), val);
            // 提交到状态同步管理
            SyncDataUpdater.commit(ownerSyncDataGetter, ownerSyncDataSetter, dataClass, owner().getId(), aid, consumer, val);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void update(Map<Long, E> datas) {
        for (E data : datas.values()) {
            __update__(data);
        }
    }

    @Override
    public void Destroy() {
        this.save2DB();
    }

    @Override
    public void syncAll() {
        try {
            for (E data : this.data.values()) {
                for (int i = 0; i < dataFieldsCache.length; i++) {
                    Field field = dataFieldsCache[i];
                    NetSyncOwner netSyncOwner = AnnotationUtils.findAnnotation(field.getType(), NetSyncOwner.class);
                    if (netSyncOwner != null && netSyncOwner.value() == false) { continue; }
                    this.data.put(data.getId(), data);
                    // 提交到状态同步管理
                    SyncDataUpdater.commit(owner().getId(), data);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public E newInstance() {
        try {
            E e = this.dataClass.newInstance();
            e.setUid(owner().getId());
            e.setId(RandomUtils.snowId());
            e.setCreateTime(LocalDateTime.now());
            e.setUpdateTime(LocalDateTime.now());
            return e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 删除，负号代表删除
    public void __del__(E data) {
        __del__(data.getId());
    }
    public void __del__(Long aid) {
        E del = this.data.remove(aid);
        del.setId(-aid);
        SyncDataUpdater.commit(owner().getId(), del);
        setDirty(-aid, true);
    }

}